package org.fhnw.aigs.client.gameHandling;
import java.io.File;
import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.fhnw.aigs.client.GUI.BaseGameWindow;
import org.fhnw.aigs.client.communication.ClientMessageBroker;
import org.fhnw.aigs.commons.GameMode;
import org.fhnw.aigs.commons.JoinType;
import org.fhnw.aigs.commons.Player;
import org.fhnw.aigs.commons.communication.JoinMessage;
import org.fhnw.aigs.commons.communication.Message;
/**
* This class is the equivalent of "Game" on the client side. However it is
* merely a container class that contains all important facts of a game:<br>
* The game name, a reference to the player, the gameMode and a reference to the
* current "BaseGameWindow". The latter allows the game to manipulate the GUI
* directly when using the standard AIGS BaseClient.<br>
* v1.0 Initial release<br>
* v1.1 Functional changes, additional properties
*
* @author Matthias Stöckli (v1.0)
* @version v1.1 (Raphael Stoeckli, 22.10.2014)
*/
public abstract class ClientGame {
/**
* The name of the game, e.g. "TicTacToe". This must be the same name as the
* package name!
*/
protected String gameName;
/**
* The name of the Party
*/
protected String partyName;
/**
* The type of starting the game respectively to join in a existing game
*/
protected JoinType joinType;
/**
* A reference to the player, this will usually be set by the
* ClientMessageBroker in the course of the identification process. Once the
* client was identified, the server sends back the definite player name
* which will be used to identify the player.
*/
protected Player player;
/**
* The game mode helps controlling the game flow. if the game mode
* "SinglePlayer" is selected, it is not possible to use the method
* "startGameWith" on the client side.
*/
protected GameMode gameMode;
/**
* The BaseGameWindow holds a reference to the gameWindow. This class is
* used in the standard JavaFX based AIGS BaseClient for showing the GUI. It
* can be accessed whenever the GUI changes on a large scale, for example
* when an overlay (loading screen, end game screen etc.) should be faded in
* or out.
*
*/
protected BaseGameWindow gameWindow;
/**
* Indicates whether the game is still running or not
*/
protected boolean noInteractionAllowed;
/**
* Indicates a (optional) version number of the game. Use this for better version control of games on the server/client
*/
protected String versionString;
/**
* See {@link ClientGame#gameWindow}
*/
public BaseGameWindow getGameWindow() {
return gameWindow;
}
/**
* See {@link ClientGame#gameName}
*/
public String getGameName() {
return gameName;
}
/**
* See {@link ClientGame#joinType}
*/
public JoinType getJoinType() {
return joinType;
}
/**
* See {@link ClientGame#partyName}
*/
public String getPartyName()
{
return partyName;
}
/**
* See {@link ClientGame#player}
*/
public Player getPlayer() {
return player;
}
/**
* See {@link ClientGame#gameMode}
*/
public GameMode getGameMode() {
return gameMode;
}
/**
* * See {@link ClientGame#noInteractionAllowed}
*/
public boolean isNoInteractionAllowed() {
return noInteractionAllowed;
}
/**
* See {@link ClientGame#versionString}.
*/
public String getVersionString() {
return versionString;
}
/**
* See {@link ClientGame#player}
*/
public void setPlayer(Player player) {
this.player = player;
}
/**
* See {@link ClientGame#gameName}
*/
public void setGameName(String gameName) {
this.gameName = gameName;
}
/**
* See {@link ClientGame#partyName}
*/
public void setPartyName(String partyName){
this.partyName = partyName;
}
/**
* See {@link ClientGame#joinType}
*/
public void setJoinType(JoinType joinType) {
this.joinType = joinType;
}
/**
* See {@link ClientGame#gameWindow}
*/
public void setGameWindow(BaseGameWindow gameWindow) {
this.gameWindow = gameWindow;
}
/**
* See {@link ClientGame#gameMode}
*/
public void setGameMode(GameMode gameMode) {
this.gameMode = gameMode;
}
/**
* * See {@link ClientGame#noInteractionAllowed}
*/
public void setNoInteractionAllowed(boolean noInteractionAllowed) {
this.noInteractionAllowed = noInteractionAllowed;
}
/**
* See {@link ClientGame#versionString}.
*/
public void setVersionString(String versionString) {
this.versionString = versionString;
}
/**
* Creates a new instance of a ClientGame with the specified name and a
* gameMode. This constructor will set up the Shutdown hook which takes care
* of the house keeping like sending RoceCloseMessages upon termination.
*
* @param gameName The game's name, e.g. TicTacToe
* @param mode The game mode, e.g. SinglePlayer
*/
public ClientGame(String gameName, GameMode mode) {
setUpLogging(); // Start Logging
this.gameName = gameName;
this.gameMode = mode;
this.joinType = JoinType.Auto; // Default
Thread shutdownCleanUpThread = new Thread(new ClientShutdownCleanUp());
shutdownCleanUpThread.setName("ShutdownCleanUpThread");
Runtime.getRuntime().addShutdownHook(shutdownCleanUpThread);
}
/**
* Creates a new instance of a ClientGame with the specified name, version and
* gameMode. This constructor will set up the Shutdown hook which takes care
* of the house keeping like sending RoceCloseMessages upon termination.
*
* @param gameName The game's name, e.g. TicTacToe
* @param mode The game mode, e.g. SinglePlayer
* @param version The version of the game
*/
public ClientGame(String gameName, String version, GameMode mode) {
setUpLogging(); // Start Logging
this.gameName = gameName;
this.gameMode = mode;
this.versionString = version;
this.joinType = JoinType.Auto; // Default
Thread shutdownCleanUpThread = new Thread(new ClientShutdownCleanUp());
shutdownCleanUpThread.setName("ShutdownCleanUpThread");
Runtime.getRuntime().addShutdownHook(shutdownCleanUpThread);
}
/**
* Creates a new instance of a ClientGame with the specified name. This
* constructor does not specify a game mode or version. It can be used when there is no
* need for a game mode. The GameMode will then be set to SinglePlayer which
* will have no effect on the game as long as not specifically implemente in
* the game logic.
*
* @param gameName The game's name, e.g. "TicTacToe"
*/
public ClientGame(String gameName) {
this(gameName, GameMode.SinglePlayer);
}
/**
* This method is the core of every game on the client side. Due to the
* abstract nature, it must be implemented by every ClientGame. The method
* is responsible for the the processing of incoming messages. It is
* intended to work in a very similar way to the <b>processGameLogic</b>
* method of the server side.<br>
* ProcessGameLogic() will be called as soon as a non system message (e.g.
* ClientShutdownMessage or similar) arrives. The
* {@link ClientMessageBroker} will forward the class to this method. In
* this method it will then be interpreted and the results will change
* whatever is needed, e.g. the GUI.
*
* @param message The message to be processed.
*/
public abstract void processGameLogic(Message message);
/**
* This <b>abstract</b> method will be called once a connection to the
* server$ has successfully been established. This method is the last
* possibility for the player to ask for any special input or similar,
* before a game starts. Usually, the onGameReady() method ends with a call
* of {@link ClientGame#startGame} which will trigger a JoinMessage.
* The server will then start a new game or let the player join an existing game.
*/
public abstract void onGameReady();
/**
* Sends a message to the server.
* <i>Please note: This method is just a shorthand. It just calls the
* ClientMessageBroker's sendMessage method which will call the method's
* sendMessage method. The same effect could be achieved by statically
* loading ClientMessageBroker.</i>
*
* @param message The message to be sent.
*/
public void sendMessageToServer(Message message) {
if (noInteractionAllowed) {
Logger.getLogger(ClientGame.class.getName()).log(Level.INFO,
"It is not possible to send messages if the flag \"noInteractionAllowed\" is set to true");
} else {
ClientMessageBroker.sendMessage(message);
}
}
/**
* Initializes the game on the server side by sending a JoinMessage.
* The Server will decide whether to create or join a game depending on
* the defined join type. The JoinMessage will contain the name of the game,
* the join type and the game mode.<br>
* This method will be called in {@link ClientGame#onGameReady()}
*/
public void startGame() {
JoinMessage joinMessage = null;
if (this.joinType == JoinType.Auto)
{
joinMessage = new JoinMessage(gameName, gameMode, JoinType.Auto);
}
else
{
joinMessage = new JoinMessage(gameName, gameMode, partyName, this.joinType);
}
sendMessageToServer(joinMessage);
}
/**
* This method is similar to the "startGame()" method. Initializes the game
* on the server side by sending a JoinMessage. The JoinMessage will contain
* the name of the game and the game mode. Additionally a player can
* indicate a {@link org.fhnw.aigs.commons.Game#partyName}. In this case the server will match all
* people who indicated the same partyName
*
* @param partyName The name of the party, e.g. "TicTacPascal"
*/
public void startNamedParty(String partyName) {
if (partyName.isEmpty()) {
Logger.getLogger(ClientGame.class.getName()).log(Level.SEVERE, "Please name the party.");
System.exit(0);
}
if (gameMode == GameMode.SinglePlayer) {
Logger.getLogger(ClientGame.class.getName()).log(Level.SEVERE, "You cannot use 'startGameAsNamedParty' if you want to play "
+ "in single player mode.");
System.exit(0);
}
JoinMessage joinMessage = new JoinMessage(gameName, gameMode, partyName, JoinType.JoinParticularGame);
sendMessageToServer(joinMessage);
}
/**
* This method is responsible for the logging by setting a FileHandler.
* Usually the log files will be saved to the folder "logs", under the name
* "aigs.log" If this is not possible, a new log file will be created.
*/
private static void setUpLogging() {
// Get the standard logger (root logger) from which all loggers inherit
Logger rootLogger = Logger.getLogger("");
try {
new File("logs").mkdir(); // Creates "logs" folder, if it does not already exist
// Housekeeping: Get the old handlers and remove them.
Handler[] handlers = rootLogger.getHandlers();
for (Handler h : handlers) {
rootLogger.removeHandler(h);
}
// Add a filehandler to the root logger. All logging activity will be saved in the file "logs/aigs.log"
// Additionally all logs shall also be shown on the console
rootLogger.addHandler(new FileHandler("logs/aigs.log"));
ConsoleHandler h = new ConsoleHandler();
h.setLevel(Level.ALL);
rootLogger.addHandler(h);
} catch (IOException ex) {
Logger.getLogger(ClientGame.class.getName()).log(Level.SEVERE, "File could not be opened or created. A new log file will be created.", ex);
} catch (SecurityException ex) {
Logger.getLogger(ClientGame.class.getName()).log(Level.SEVERE, null, ex);
}
catch (Exception ex) // All other Exceptions
{
Logger.getLogger(ClientGame.class.getName()).log(Level.SEVERE, "An unknown error occurred.", ex);
}
Logger.getLogger(ClientGame.class.getName()).log(Level.INFO, "Logging was set up.");
}
}